package cc.jq1024.middleware.encrypt.util;


import cc.jq1024.middleware.encrypt.domain.entity.KeyPairs;
import cc.jq1024.middleware.encrypt.domain.valobj.Algorithm;
import cc.jq1024.middleware.encrypt.exception.AlgorithmException;
import cc.jq1024.middleware.encrypt.exception.DecryptException;
import cc.jq1024.middleware.encrypt.exception.EncryptException;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSA 加密解密
 * @author li--jiaqiang
 */
public class RSA {
    /**
     * 签名算法
     */
    public static String RSA_ALGORITHM = Algorithm.RSA;

    public static KeyPairs createKeys(int keySize) throws AlgorithmException {
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            kpg.initialize(keySize);
            KeyPair keyPair = kpg.generateKeyPair();
            String publicKeyStr = Base64.getMimeEncoder().encodeToString(keyPair.getPublic().getEncoded());
            String privateKeyStr = Base64.getMimeEncoder().encodeToString(keyPair.getPrivate().getEncoded());
            return new KeyPairs(publicKeyStr,privateKeyStr);
        } catch (Exception e) {
            throw new AlgorithmException(e);
        }
    }

    public static RSAPublicKey getPublicKey(String publicKey) throws AlgorithmException {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.getMimeDecoder().decode(publicKey));
            return (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        } catch (GeneralSecurityException e) {
            throw new AlgorithmException(e);
        }
    }

    public static RSAPrivateKey getPrivateKey(String privateKey) throws AlgorithmException {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.getMimeDecoder().decode(privateKey));
            return (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        } catch (GeneralSecurityException e) {
            throw new AlgorithmException(e);
        }
    }

    public static String encryptPublicKey(String content, RSAPublicKey publicKey) throws EncryptException {
        try{
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return Base64.getEncoder().encodeToString(rsa(cipher, Cipher.ENCRYPT_MODE, content.getBytes(StandardCharsets.UTF_8), publicKey.getModulus().bitLength()));
        }catch(Exception e){
            throw new EncryptException(e);
        }
    }

    public static String encryptPrivateKey(String content, RSAPrivateKey privateKey) throws EncryptException {
        try{
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            return Base64.getEncoder().encodeToString(rsa(cipher, Cipher.ENCRYPT_MODE, content.getBytes(StandardCharsets.UTF_8), privateKey.getModulus().bitLength()));
        } catch (Exception e){
            throw new EncryptException(e);
        }
    }

    public static String decrypt(String data, RSAPrivateKey privateKey) throws DecryptException {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return new String(rsa(cipher, Cipher.DECRYPT_MODE, Base64.getMimeDecoder().decode(data), privateKey.getModulus().bitLength()), StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new DecryptException(e);
        }
    }

    public static String decrypt(String data, RSAPublicKey publicKey) throws DecryptException {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            return new String(rsa(cipher, Cipher.DECRYPT_MODE, Base64.getMimeDecoder().decode(data), publicKey.getModulus().bitLength()), StandardCharsets.UTF_8);
        } catch (Exception e){
            throw new DecryptException(e);
        }
    }


    private static byte[] rsa(Cipher cipher, int mode, byte[] contents, int keySize) throws Exception {
        int maxBlock = 0;
        if(mode == Cipher.ENCRYPT_MODE){
            maxBlock = keySize / 8 - 11;
        }else{
            maxBlock = keySize / 8;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] buff;
        int i = 0;
        try{
            while(contents.length > offSet){
                if(contents.length- offSet > maxBlock){
                    buff = cipher.doFinal(contents, offSet, maxBlock);
                }else{
                    buff = cipher.doFinal(contents, offSet, contents.length - offSet);
                }
                out.write(buff, 0, buff.length);
                i++;
                offSet = i * maxBlock;
            }
        }catch(Exception e){
            throw new RuntimeException(e);
        }
        byte[] results = out.toByteArray();
        out.close();
        return results;
    }

    public static void main(String[] args) throws AlgorithmException, DecryptException, EncryptException {
        KeyPairs keys = RSA.createKeys(2048);
        String privateKey = keys.getPrivateKey();
        String publicKey = keys.getPublicKey();
        System.out.println(privateKey);
        System.out.println();
        System.out.println();
        System.out.println();
        System.out.println(publicKey);

//        String s = RSA.encryptPrivateKey("ljq@security.key", RSA.getPrivateKey("MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAIIQVh0hnz3aYR0tBc41/8B+aGSg" +
//                "lKrZvRVo2AJhifsgikf9UrBY1fnBa5sbQ8jCwWSJ4mDDDzFKNroqArO0hvah6lvRiy897TKVdS/x" +
//                "FVYM5wWj5lhBGNuJW/foHIYrsR/5m01iSEXOCI7S7VcafTO4nKfHvcz6sBbimU1bLMxnAgMBAAEC" +
//                "gYBN9ub33XCsThyKtbNWMi5VwDBpqYyUgaMDk25wtvFUNORPVP3dMdNKPHR5bLWMQZpV3wVxRVv1" +
//                "CuyVWZt3B48ip+SKXZ9FPDXGERw2aEOsM00WU4hO3AadZbc78oTJfSJ7Tpjjq11R/tO48molSBG7" +
//                "Md3fz8pWa4KAEfSq3A3D+QJBANs+bAFR7dQiI5saW0iaFQbnSLwbSQ+H56qyS8oHNIkevdGOfhnt" +
//                "6+V9IXXFujUVTbT3gKVxcAw/SXaxWwnWVkMCQQCX3nUWYD6bqEI5bQ95CNClmUnH/sIxVBr/xNd8" +
//                "fuzJVquGZqGTCdTJ6ZkuCJBWY3cvPWz0KR7ZkaDxjDpv/LkNAkEAoM6vH2S9YTvNC+WQELdbNt4L" +
//                "PlVxCCAxrsTpy/vu76gJqBuwqEoXHRLjqONnSaoffd2PkUmb2o/MzW8WjCeWywJAYU0ZDYYKduZd" +
//                "z7ctU6mvLHpCyE1UFBDD3505Rr13biQGb2zwFmoVGGx/RPwdpgvCP9UGKoTyDAl+6WOjyT5NPQJB" +
//                "AIFL36K+0nK2pWpdBazpmNwePHw06fAdyhKybaBJegRcCcPf7vw9vdadDvzyO1YcMIwNTUyvhQlT" +
//                "94kxcNzU67I="));
//
//        // AVPudVApFzVK/lxnNXGccXYeJxV5X54zawj6fBfRkHCjTSv+V4Lg86/XZihCsDuM+w4EzdGpc41yIzMLKcWs+tYZu78SlBOGZg9oXmRC6Wr6SM7My2NaDxrrcBNYahA5wCo772Seccb/zJkECQwynEwZlInq08IVYOTCudUCEE4=
//        System.out.println(s);
    }
}
